package com.compunit.composite.core.cx;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import com.compunit.composite.core.cx.enumn.EActivationTag;
import com.compunit.composite.core.cx.enumn.EBindingTag;
import com.compunit.composite.core.cx.enumn.EClassTag;
import com.compunit.composite.core.cx.enumn.EComponentTag;
import com.compunit.composite.core.cx.enumn.EConsumerTag;
import com.compunit.composite.core.cx.enumn.EPMappingTag;
import com.compunit.composite.core.cx.enumn.EProviderTag;
import com.compunit.composite.core.cx.enumn.ERMappingTag;
import com.compunit.composite.core.cx.enumn.EUnitTag;
import com.compunit.composite.core.interfaces.cx.ICxBinding;
import com.compunit.composite.core.interfaces.cx.ICxImporter;
import com.compunit.composite.core.interfaces.cx.ICxMapping;
import com.compunit.composite.core.interfaces.cx.ICxUnit;
import com.compunit.composite.core.interfaces.xml.IXMLParser;
import com.compunit.composite.core.interfaces.xml.IXMLTag;
import com.compunit.composite.core.xml.XMLParser;

/**
 * <code>CXImporter</code> defines the implementation of
 * an utility unit that imports the essential data from
 * a given component cx definition file.
 *
 * @author Yi Wang (Neakor)
 * @version Creation date: 11-06-2009 17:59 EST
 * @version Modified date: 11-21-2009 18:43 EST
 */
public class CxImporter implements ICxImporter {
	/**
	 * The <code>IXMLParser</code>.
	 */
	private final IXMLParser parser;
	/**
	 * The <code>List</code> of <code>ICxUnit</code>
	 * in the activation order.
	 */
	private final List<ICxUnit> activation;
	/**
	 * The <code>List</code> of <code>ICxMapping</code> in
	 * the processed component.
	 */
	private final List<ICxMapping> mappings;
	/**
	 * The <code>List</code> of <code>IBinding</code> in
	 * the processed component.
	 */
	private final List<ICxBinding> bindings;
	/**
	 * The <code>String</code> name of the parsed component.
	 */
	private String name;

	/**
	 * Constructor of <code>CXImporter</code>.
	 */
	public CxImporter() {
		this.parser = new XMLParser();
		this.activation = new ArrayList<ICxUnit>();
		this.mappings = new ArrayList<ICxMapping>();
		this.bindings = new ArrayList<ICxBinding>();
	}

	@Override
	public void process(URL url) throws IOException {
		// Initialize parser and loader.
		this.cleanup();
		this.parser.process(url);
		// Retrieve data from cx file.
		final IXMLTag root = this.parser.getRoot();
		this.name = root.getValue(EComponentTag.Key_Name.getString());
		// Extract activation, mappings and bindings.
		this.extractActivation(root);
		this.extractMapping(root);
		this.extractBinding(root);
	}

	/**
	 * Extract the activation order of class names.
	 * @param root The root <code>IXMLTag</code> containing
	 * activation information.
	 */
	private final void extractActivation(final IXMLTag root) {
		// There is only one activation tag.
		IXMLTag atag = null;
		for(final IXMLTag child : root.getChildren()) {
			if(child.getName().equals(EActivationTag.Name.getString())) {
				atag = child;
				break;
			}
		}
		// Get all class names.
		for(final IXMLTag child : atag.getChildren()) {
			if(child.getName().equals(EUnitTag.Name.getString())) {
				final CxUnit unit = new CxUnit();
				unit.setClassname(child.getValue(EUnitTag.Key_Classname.getString()));
				unit.setResourcename(child.getValue(EUnitTag.Key_Resourcename.getString()));
				this.activation.add(unit);
			}
		}
	}

	/**
	 * Extract the mapping data from the given XML tag.
	 * @param root The root <code>IXMLTag</code> containing
	 * binding information.
	 */
	private final void extractMapping(final IXMLTag root) {
		for(final IXMLTag mtag : root.getChildren()) {
			final String name = mtag.getName();
			if(name.equals(EPMappingTag.Name.getString()) || name.equals(ERMappingTag.Name.getString())) {
				boolean provided = false;
				if(name.equals(EPMappingTag.Name.getString())) provided = true;
				final CxMapping mapping = new CxMapping(provided);
				if(provided) mapping.setInterface(mtag.getValue(EPMappingTag.Key_Interfacename.getString()));
				else mapping.setInterface(mtag.getValue(ERMappingTag.Key_Interfacename.getString()));
				for(final IXMLTag classtag : mtag.getChildren()) {
					mapping.addClass(classtag.getValue(EClassTag.Key_Classname.getString()));
				}
				this.mappings.add(mapping);
			}
		}
	}

	/**
	 * Extract the binding pairs from given XML tag.
	 * @param root The root <code>IXMLTag</code> containing
	 * binding information.
	 */
	private final void extractBinding(final IXMLTag root) {
		for(final IXMLTag btag : root.getChildren()) {
			if(btag.getName().equalsIgnoreCase(EBindingTag.Name.getString())) {
				// There should be only two children.
				final CxBinding binding = new CxBinding();
				for(final IXMLTag prtag : btag.getChildren()) {
					final String prname = prtag.getName();
					if(prname.equalsIgnoreCase(EProviderTag.Name.getString())) {
						final String classname = prtag.getValue(EProviderTag.Key_Classname.getString());
						binding.setProvider(classname);
					} else if(prname.equalsIgnoreCase(EConsumerTag.Name.getString())) {
						final String classname = prtag.getValue(EConsumerTag.Key_Classname.getString());
						binding.setConsumer(classname);
					}
				}
				this.bindings.add(binding);
			}
		}
	}
	
	@Override
	public String getName() {
		return this.name;
	}

	@Override
	public Iterable<ICxUnit> getActivation() {
		return this.activation;
	}

	@Override
	public Iterable<ICxMapping> getMappings() {
		return this.mappings;
	}

	@Override
	public Iterable<ICxBinding> getBindings() {
		return this.bindings;
	}

	@Override
	public void cleanup() {
		this.parser.cleanup();
		this.activation.clear();
		this.mappings.clear();
		this.bindings.clear();
		this.name = null;
	}
}
